#ifndef _CONTEXT_H
#define _CONTEXT_H

extern Shader shader  ;

class Context 
{
public:
	Context () {}
	void load_model (Mesh * m) {mesh = m;face_size = mesh->v_faces.size() ;}
	void set_sample_color (unsigned face_idx, unsigned sample_idx, RGB color )
	{
		this->mesh->v_faces[face_idx]->samples[sample_idx].color = color; 
	}

	void draw_triangles (Point point[3], GLfloat color [40], int res, GLfloat k, GLfloat h, GLfloat c)
	{
		shader.set_uniform("res", res);
		shader.set_uniform("color", color, (res + 1 ) * (res + 2) / 2);
		shader.set_uniform("k", k); 
		shader.set_uniform("h", h);
		shader.set_uniform("c", c);

		shader.set_uniform("draw_face", 1); 

		glBegin(GL_TRIANGLES);
		glTexCoord2f(0.0, 0.0); glVertex3f(point[0].x, point[0].y, point[0].z);
		glTexCoord2f(c, 0.0); glVertex3f(point[1].x, point[1].y, point[1].z);
		glTexCoord2f(k, h); glVertex3f(point[2].x, point[2].y, point[2].z);
		glEnd();
		glFlush();
		shader.set_uniform("draw_face", 0); 
	}
	void draw_faces (unsigned idx) 
	{
		Face * face = mesh->v_faces[idx] ;

		//Prepare shader begin
		//shader.set_uniform("draw_face", 1); 
		
		GLfloat color1 [60] ;
		for (int i = 0; i < 15; ++i)
		{
			color1 [4*i + 0] = face->samples[i].color.r / 255.0f;
			color1 [4*i + 1] = face->samples[i].color.g / 255.0f;
			color1 [4*i + 2] = face->samples[i].color.b / 255.0f;
			color1 [4*i + 3] = 1.0f;
		}

		Point p[3];

		p[0] = mesh->v_faces[idx]->he->from_v->point ;
		p[1] = mesh->v_faces[idx]->he->next->from_v->point ;
		p[2] = mesh->v_faces[idx]->he->next->next->from_v->point ;
		RGB color = mesh->v_faces[idx]->color ;
		
		if (mesh->v_faces[idx]->selected)
		{
			glColor3ub(255 - color.r, 255 - color.g, 255 - color.b);
		}
		else
		{
			glColor3ub(color.r, color.g, color.b);
		}
		
		Point n = Point::normal((p[1]-p[0])^(p[2]-p[0])) ;
		glNormal3f (n.x, n.y, n.z);

		GLfloat k = compute_k(p); 
		GLfloat h = compute_h(p); 
		GLfloat c = compute_c(p); 
		
		if (h >=c)
		{
			draw_triangles(p, color1, 4, k / h, 1.0, c /h );
		}
		else
		{
			draw_triangles(p, color1, 4, k / c, h / c, 1.0 );
		}
		if (this->states.sample_points)
		{
			unsigned count = 0;
			unsigned res = mesh->v_faces[idx]->resolution;
			for (unsigned i = 0 ; i <= res; ++i)
			{
				for (unsigned j = 0 ; j<= res-i; ++j)
				{
					/*
					Point p = p1 * (float (i)/ res) + p2 * (float (j)/ res) + p3 * (float(res-i-j)/res) ;
					glColor3f(1.0, 1.0, 0);
					*/
					Point p = face->samples[count].position;
					RGB color = face->samples[count].color ;
					count++;
					glColor3ub(color.r, color.g , color.b);
					drawsphere(p.x, p.y, p.z, 0.02); 
				}
			}
		}
		glFlush();
	}
	void draw_model () 
	{
		unsigned size = mesh->v_faces.size();
		for (unsigned i = 0; i < size; ++i)
		{
			draw_faces (i);
		}
	}
	RGB get_face_color (unsigned idx) const {return mesh->v_faces[idx]->color;} 
	void set_face_color (unsigned idx, RGB & color) {mesh->v_faces[idx]->color = color ;}
	void rand_face_color()
	{
		for (std::vector<Face *>::iterator iter = mesh->v_faces.begin(); iter!= mesh->v_faces.end(); ++iter)
		{
			RGB color ;
			rand_color(color);
			(*iter)->color = color; 
			(*iter)->make_samples(); 
		}
	}
	
	void draw_backbuffer(unsigned long numPrims)
	{
		glDisable (GL_BLEND);
		glDisable (GL_DITHER);
		glDisable (GL_FOG);
		glDisable (GL_LIGHTING);
		glDisable (GL_TEXTURE_1D);
		glDisable (GL_TEXTURE_2D);
		glDisable (GL_TEXTURE_3D);
		glShadeModel (GL_FLAT);

		GLint redBits, greenBits, blueBits;

		glGetIntegerv (GL_RED_BITS, &redBits);
		glGetIntegerv (GL_GREEN_BITS, &greenBits);
		glGetIntegerv (GL_BLUE_BITS, &blueBits);
		/*
		std::cout<<"red   "<<redBits<<std::endl;
		std::cout<<"green "<<greenBits<<std::endl;
		std::cout<<"blue  "<<blueBits<<std::endl;
		*/

		GLuint redMask = makeMask(redBits) << (greenBits + blueBits);
		GLuint greenMask = makeMask(greenBits) << blueBits;
		GLuint blueMask = makeMask(blueBits);
		/*
		std::cout<<"red   M "<<std::bitset<32> (int(redMask))<<std::endl;
		std::cout<<"green M "<<std::bitset<32> (int(greenMask))<<std::endl;
		std::cout<<"blue  M "<<std::bitset<32> (int(blueMask))<<std::endl;
		*/

		int redShift = greenBits + blueBits;
		int greenShift = blueBits; 
		unsigned idx;
		for (idx=0; idx < face_size; idx++) 
		{
			//Each face is encoded as 0x00(RED)(GREEN)(BLUE)
			//0x00 (face_idx + 1)
			unsigned red   = ((idx+1) & redMask) >> redShift;
			unsigned green = ((idx+1) & greenMask) >> greenShift;
			unsigned blue  = (idx+1) & blueMask ;
			
			Point p1 = mesh->v_faces[idx]->he->from_v->point ;
			Point p2 = mesh->v_faces[idx]->he->next->from_v->point ;
			Point p3 = mesh->v_faces[idx]->he->next->next->from_v->point ;
			RGB color = mesh->v_faces[idx]->color ;
			glBegin(GL_TRIANGLES);
			glColor4ub(red, green, blue, 0);

			Point n = Point::normal((p2-p1)^(p3-p1)) ;
			glNormal3f (n.x, n.y, n.z);
			glVertex3f( p1.x, p1.y, p1.z);				// Top
			glVertex3f( p2.x, p2.y, p2.z);				// Bottom Left
			glVertex3f( p3.x, p3.y, p3.z);				// Bottom Right
			//glColor3f(randreal(), randreal() , randreal());
			glEnd();

			//next we draw the sample points
			//Each samples points is encoded as 0x(samples_point_idx + 1)[8] (face_idx +1)[24]
			
			unsigned res = mesh->v_faces[idx]->resolution;
			for (int j = 0; j < (res + 1)*(res + 2) /2 ; ++j)
			{
				glColor4ub(red,green,blue, j+1);
				Point p = mesh->v_faces[idx]->samples[j].position ;
				drawsphere(p.x, p.y, p.z, 0.02); 
			}
			
		}

	}
	bool get_sample_state() {return states.sample_points ;}
	void set_samples() {this->states.sample_points = true; }
	void unset_samples()  {this->states.sample_points = false; }
private:
	GLuint makeMask(GLint bits)
	{
		return 255;
		/*
		GLuint num = 1;
		for (GLint i = 0; i < bits; ++i)
		{
		num *= 2;
		}
		num -= 1;
		return num;
		*/
	}
	GLfloat compute_k(Point p[3])
	{
		float a = (p[1]-p[2]).length(); 
		float b = (p[2]-p[0]).length();
		float c = (p[0]-p[1]).length();
		float k = (b*b + c * c - a * a) / (2.0 * c) ; 
		return GLfloat (k); 
	}
	GLfloat compute_h(Point p[3])
	{
		float a = (p[1]-p[2]).length(); 
		float b = (p[2]-p[0]).length();
		float c = (p[0]-p[1]).length();
		float cos_a = (b*b + c* c - a* a)/ (2.0 * b * c) ;
		float sin_a = sqrt (1.0 - cos_a * cos_a) ;
		return (b * sin_a) ;
	}
	GLfloat compute_c(Point p[3])
	{
		return (p[0] - p[1]).length();
	}
private:
	Mesh * mesh; 
	unsigned face_size ;
	STATES states;
};

#endif